﻿using ServerListerner.Connections;
using ServerListerner.Helper;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace ServerListerner
{
    /// <summary>
    /// Listen To TCP
    /// </summary>
    public class TCPServer: IDisposable
    {
        private readonly int _port;
        private readonly IPAddress _ipaddr;
        private readonly Socket _socket;
        private Action<ITCPConnection> _action;
        public bool IsRunning = false;
        public TCPServer(int port)
        { 
            this._port = port;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         }
        public TCPServer(IPAddress ipaddr, int port) : this(port)
        {
            this._ipaddr = ipaddr;
        }
        public TCPServer(string ip, int port) : this(IPAddress.Parse(ip),port) { }
       // public bool StartListen(Action<T> action)
        public bool StartListen(Action<ITCPConnection> action) 
        {
            try
            {
                if (_port.PortInUse())
                    return false;
                IsRunning = true;
                _action = action;
                IPAddress iPAddress = _ipaddr == null || _ipaddr == IPAddress.None ? IPAddress.Any : _ipaddr;
                _socket.Bind(new IPEndPoint(iPAddress, this._port));
                _socket.Listen(int.MaxValue);
                if (!_port.PortInUse())
                    return false;
                StartTCPAccept();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }

        }
        public bool StopListen()
        {
            IsRunning = false;
            this.CloseSocket();
            return !_port.PortInUse();
        }
        private void StartTCPAccept() 
          =>   this._socket.BeginAccept(TCPAccept, this._socket);
        private void TCPAccept(IAsyncResult iar) {
            try
            {
                if (!IsRunning) return;
                if(iar.IsCompleted && iar.AsyncState != null)
                {
                    Socket client = _socket.EndAccept(iar);
                    if (client.Connected)
                    {
                        new TCPConnection(client, _action);
                    }
                }
            }
            finally
            {
                StartTCPAccept();
            }
        }
        public void Dispose()
        {
            this.CloseSocket();
            _action = null;
        }
        private void CloseSocket()
        {
            this._socket.Shutdown(SocketShutdown.Both);
            this._socket.Close();
            this._socket.Dispose();
        }
    }
}
